// 考勤模块
const logger = require("../utils/logger")
const mysqlUtil = require("../utils/mysqlUtil")
const redisUtil = require("../utils/redisUtil")
const util = require("../utils/util")
const commonModule = require("./common")
const moment = require('moment')
const config = require("../config")
const { getLingdao } = require("./task")
const xlsx = require('node-xlsx').default
const path = require('path')
const fs = require('fs')

let deductionTime = function(startTime, endTime) {
    let period = []
    const start = moment(startTime, 'YYYY-MM-DD HH:mm:ss');
    const end = moment(endTime, 'YYYY-MM-DD HH:mm:ss');

    const deductionStart = moment({ hour: 17, minute: 0 });
    const deductionEnd = moment({ hour: 9, minute: 0 }).add(1, 'day');

    const currentDate = start.clone();
    while (currentDate.isBefore(end)) {
        const deductionDateStart = moment({
            year: currentDate.year(),
            month: currentDate.month(),
            date: currentDate.date(),
            hour: deductionStart.hour(),
            minute: deductionStart.minute(),
        });

        const deductionDateEnd = moment({
            year: currentDate.year(),
            month: currentDate.month(),
            date: currentDate.date() + 1,
            hour: deductionEnd.hour(),
            minute: deductionEnd.minute(),
        });

        if (deductionDateStart.isBefore(end) && deductionDateEnd.isAfter(start)) {
            const deductionStartFormatted = deductionDateStart.format('YYYY-MM-DD HH:mm:ss');
            const deductionEndFormatted = deductionDateEnd.format('YYYY-MM-DD HH:mm:ss');
            period.push({start: deductionStartFormatted, end: deductionEndFormatted})
        }

        currentDate.add(1, 'day');
    }

    return period
}

module.exports = {
    // 今日打卡详情
    todayDakaDetail: async (io, socket, params, callback) => {

        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc

        try {
            let nowDate = moment().format('YYYY-MM-DD')

            // 获取今天打卡信息
            let res = await mysqlUtil.execute('SELECT * FROM tb_user_daka WHERE uid = ? AND day = ? LIMIT 1', [uid, nowDate])
            let todayDaka = {
                uid,
                day: nowDate,
                shangbanTime: -1,
                shangbanTimeRes: 0, // 0正常 1迟到 2严重迟到 3迟到旷工
                shangbanBukaSta: 0, // 0未补卡 1审核中 2通过 3失败
                xiabanTime: -1,
                xiabanTimeRes: 0, // 0正常 1早退 2严重早退 3早退旷工
                xiabanBukaSta: 0, // 0未补卡 1审核中 2通过 3失败
                createTime: Date.now()
            }
            if (res.length == 0) {
                await mysqlUtil.execute('INSERT INTO tb_user_daka SET ?', [todayDaka])
            } else {
                todayDaka = res[0]
            }

            // 查询本月补卡次数
            res = await mysqlUtil.execute('SELECT COUNT(1) cnt FROM tb_user_buka_apply WHERE uid = ? AND createTime > ?', [uid, moment().startOf('month').valueOf()])
            todayDaka.bukaTimes = res[0].cnt
            todayDaka.day = moment(todayDaka.day).format('YYYY-MM-DD')

            // 查看打卡信息
            // res = await mysqlUtil.execute('SELECT * FROM tb_user_daka WHERE uid = ? AND day = ? LIMIT 1', [uid, moment().format('YYYY-MM-DD')])
            // let dakaInfo = res[0]
            // dakaInfo.bukaTimes = 1
            // dakaInfo.day = moment(dakaInfo.day).format('YYYY-MM-DD')
            callback({code: 0, message: 'success', data: todayDaka})
        } catch (err) {
            logger.error(err)
            return callback({code: -1, message: 'system error'})
        }
    },

    // daka: async (io, socket, params, callback) => {

    //     let uid = socket.uid
    //     if (!uid) return callback({code: 9999, message: '请重新登录'})

    //     callback = callback || commonModule.emptyFunc

    //     if (!params.longitude || !params.latitude) {
    //         return callback({code: -1, message: '参数缺失'})
    //     }

    //     try {
    //         // 查询考勤设置
    //         let res = await mysqlUtil.execute('SELECT val FROM con_system_param WHERE name = "KAOQIN_SHEZHI" LIMIT 1')
    //         if (res.length == 0) return {code: -1, message: '未设置考勤，请联系管理员'}
    //         let kaoqinSetting = JSON.parse(res[0].val)

    //         let hour = new Date().getHours()
    //         if (hour < 12) { // 上班打开

    //         } else {

    //         }

    //         let sql = 'INSERT INTO tb_user_daka_record SET ?'
    //         await mysqlUtil.execute(sql, [{uid, longitude: params.longitude, latitude: params.latitude, addr: params.addr || '', createTime: Date.now()}])
    //         callback({code: 0, message: 'success'})
    //     } catch (err) {
    //         logger.error(err)
    //         callback({code: -1, message: 'system error'})
    //     }
    // },

    // 打卡
    daka: async (io, socket, params, callback) => {
        
        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc

        logger.debug('daka', params)

        let lockKey = 'DAKA:LOCK'
        try {
            // 先加锁，防止重复打卡
            await redisUtil.setLock(lockKey)
            // 查询考勤设置
            let res = await mysqlUtil.execute('SELECT val FROM con_system_param WHERE name = "KAOQIN_SHEZHI" LIMIT 1')
            if (res.length == 0) return {code: -1, message: '未设置考勤，请联系管理员'}
            let kaoqinSetting = JSON.parse(res[0].val)

            let nowDate = moment().format('YYYY-MM-DD')

            // 获取今天打卡信息
            res = await mysqlUtil.execute('SELECT * FROM tb_user_daka WHERE uid = ? AND day = ? LIMIT 1', [uid, nowDate])
            let todayDaka = {
                uid,
                day: nowDate,
                shangbanTime: -1,
                shangbanTimeRes: 0, // 0正常 1迟到 2严重迟到 3迟到旷工
                shangbanBukaSta: 0, // 0未补卡 1审核中 2通过 3失败
                xiabanTime: -1,
                xiabanTimeRes: 0, // 0正常 1早退 2严重早退 3早退旷工
                xiabanBukaSta: 0, // 0未补卡 1审核中 2通过 3失败
                createTime: Date.now()
            }
            if (res.length == 0) {
                await mysqlUtil.execute('INSERT INTO tb_user_daka SET ?', [todayDaka])
            } else {
                todayDaka = res[0]
            }

            if (params.dakaType == 1) { // 上班卡
                if (todayDaka.shangbanTime != -1) { // 上班卡不能覆盖
                    return callback({code: -1, message: '上班卡已打卡'})
                }

                // 判断是否在打卡范围
                let distance = util.getDistance(params.latitude, params.longitude, kaoqinSetting.dakaLatitude, kaoqinSetting.dakaLongitude)
                console.log('distance', distance, kaoqinSetting.dakaFanwei)
                // 超出范围 外勤打卡
                let waiqin = 0
                if (distance > kaoqinSetting.dakaFanwei) {
                    waiqin = 1
                    // await mysqlUtil.execute('UPDATE tb_user_daka SET shangbanWaiqin = 1, shangbanTime = ?, shangbanAddress = ? WHERE uid = ? AND day = ? LIMIT 1', [Date.now(), params.address || '', uid, nowDate])
                    // return callback({code: 0, message: ''})
                } else {
                    params.address = kaoqinSetting.dakaAddress || params.address || ''
                }

                // 判断是否迟到
                let shangbanTime = nowDate + ' ' + kaoqinSetting.shangbanTime
                let shangbanTimeStamp = moment(shangbanTime).valueOf()
                let diff = Date.now() - shangbanTimeStamp
                todayDaka.shangbanTimeRes = 0
                if (diff > kaoqinSetting.chidao3Minutes * 60 * 1000) { // 迟到旷工
                    todayDaka.shangbanTimeRes = 3
                } else if (diff > kaoqinSetting.chidao2Minutes * 60 * 1000) { // 严重迟到
                    todayDaka.shangbanTimeRes = 2
                } else if (diff > kaoqinSetting.chidao1Minutes * 60 * 1000) { // 迟到
                    todayDaka.shangbanTimeRes = 1
                }

                let sql = `
                    UPDATE tb_user_daka SET 
                        shangbanTime = ?,
                        shangbanTimeRes = ?,
                        shangbanAddress = ?,
                        shangbanWaiqin = ?
                    WHERE
                        uid = ? AND day = ?
                    LIMIT 1`
                await mysqlUtil.execute(sql, [Date.now(), todayDaka.shangbanTimeRes, params.address || '', waiqin, uid, nowDate])

                // 插入打卡记录
                await mysqlUtil.execute('INSERT INTO tb_user_daka_record SET ?', [{uid, longitude: params.longitude, latitude: params.latitude, createTime: Date.now()}])
                return callback({code: 0, message: 'success'})
            } else if (params.dakaType == 2) { // 下班卡

                // 判断是否在打卡范围
                let distance = util.getDistance(params.latitude, params.longitude, kaoqinSetting.dakaLatitude, kaoqinSetting.dakaLongitude)
                console.log('distance', distance, kaoqinSetting.dakaFanwei)
                let waiqin = 0
                if (distance > kaoqinSetting.dakaFanwei) {
                    waiqin = 1
                    // await mysqlUtil.execute('UPDATE tb_user_daka SET xiabanWaiqin = 1, xiabanTime = ?, xiabanAddress = ? WHERE uid = ? AND day = ? LIMIT 1', [Date.now(), params.address || '', uid, nowDate])
                    // return callback({code: 0, message: ''})
                } else {
                    params.address = kaoqinSetting.dakaAddress || params.address || ''
                }

                // 判断是否早退
                let xiabanTime = nowDate + ' ' + kaoqinSetting.xiabanTime
                let xiabanTimeStamp = moment(xiabanTime).valueOf()
                let diff = xiabanTimeStamp - Date.now()
                todayDaka.xiabanTimeRes = 0
                if (diff > kaoqinSetting.chidao3Minutes * 60 * 1000) { // 早退旷工
                    todayDaka.xiabanTimeRes = 3
                } else if (diff > kaoqinSetting.chidao2Minutes * 60 * 1000) { // 严重早退
                    todayDaka.xiabanTimeRes = 2
                } else if (diff > kaoqinSetting.chidao1Minutes * 60 * 1000) { // 早退
                    todayDaka.xiabanTimeRes = 1
                }

                let sql = 'UPDATE tb_user_daka SET xiabanTime = ?, xiabanTimeRes = ?, xiabanAddress = ?, xiabanWaiqin = ? WHERE uid = ? AND day = ? LIMIT 1'
                await mysqlUtil.execute(sql, [Date.now(), todayDaka.xiabanTimeRes, params.address || '', waiqin, uid, nowDate])

                // 插入打卡记录
                await mysqlUtil.execute('INSERT INTO tb_user_daka_record SET ?', [{uid, longitude: params.longitude, latitude: params.latitude, createTime: Date.now()}])

                await redisUtil.clearLock(lockKey)
                return callback({code: 0, message: 'success'})
            }
        } catch (err) {
            logger.error(err)
            redisUtil.clearLock(lockKey)
            callback({code: -1, message: 'system error'})
        }
    },

    // 打卡列表
    dakaList: async (io, socket, params, callback) => {
        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc

        try {
            let res = await mysqlUtil.execute('SELECT * FROM tb_user_daka WHERE uid = ? AND day >= ? AND day <= ? ORDER BY day DESC', [uid, moment().add(-15, 'day').format('YYYY-MM-DD'), moment().add(0, 'day').format('YYYY-MM-DD')])
            callback({code: 0, message: '', data: res})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    bukaApply: async (io, socket, params, callback) => {
        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc

        let auditorsObj = {}
        for (let i = 0; i != params.auditors.length; i++) {
            auditorsObj['auditor' + i] = { uid: params.auditors[i].uid, stepName: params.auditors[i].stepName, sta: 0 }
        }

        try {
            let res = await mysqlUtil.execute('SELECT name FROM tb_user WHERE uid = ? LIMIT 1', [uid])
            if (res.length == 0) return callback({code: -1, message: '玩家不存在'})
            let myName = res[0].name

            // 查询考勤设置
            res = await mysqlUtil.execute('SELECT val FROM con_system_param WHERE name = "KAOQIN_SHEZHI" LIMIT 1')
            if (res.length == 0) return {code: -1, message: '未设置考勤，请联系管理员'}
            let kaoqinSetting = JSON.parse(res[0].val)

            // 查询本月补卡次数
            res = await mysqlUtil.execute('SELECT COUNT(1) cnt FROM tb_user_buka_apply WHERE uid = ? AND createTime > ?', [uid, moment().startOf('month').valueOf()])
            let bukaTimes = res[0].cnt

            if (bukaTimes >= kaoqinSetting.bukaMaxTimes) return callback({code: -1, message: '本月补卡次数用完'})

            let sql = 'INSERT INTO tb_user_buka_apply SET ?'
            let sqlParams = [{
                uid,
                sta: 0,
                auditSta: 1,
                day: params.day,
                bukaType: params.bukaType,
                department: params.department,
                reason: params.reason,
                pics: JSON.stringify(params.pics || []),
                auditors: JSON.stringify(auditorsObj),
                relevenceId: params.id,
                createTime: Date.now()
            }]
            res = await mysqlUtil.execute(sql, sqlParams)

            let applyId = res.insertId

            // 为审核人员添加任务
            await mysqlUtil.execute('INSERT INTO tb_user_task SET ?', [{
                uid: params.auditors[0].uid,
                typ: 19,
                relevenceId: applyId,
                pos: 0,
                title: '补卡申请',
                content: myName + '的补卡申请需要您审批',
                sta: 0,
                createTime: Date.now()
            }])
            io.in(params.auditors[0].uid + '-app').emit('TASK-CHANGE')
            io.in(params.auditors[0].uid + '-app').emit('TASK-NEW')
            io.in(params.auditors[0].uid + '-web').emit('TASK-CHANGE')
            io.in(params.auditors[0].uid + '-web').emit('TASK-NEW')
            util.sendReminderSmsToUser(params.auditors[0].uid, '补卡申请', '待审批')

            // 更改补卡状态
            if (params.bukaType == 1) {
                await mysqlUtil.execute('UPDATE tb_user_daka SET shangbanBukaSta = 1 WHERE id = ? LIMIT 1', [params.id])
            } else {
                await mysqlUtil.execute('UPDATE tb_user_daka SET xiabanBukaSta = 1 WHERE id = ? LIMIT 1', [params.id])
            }

            callback({code: 0, message: 'success'})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    // 撤销补卡申请
    revertBukaApply: async (io, socket, params, callback) => {
        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc
        if (!params.id) return callback({code: -1, message: '参数缺失'})

        try {
            let res = await mysqlUtil.execute('UPDATE tb_user_buka_apply SET chexiao = 1 WHERE id = ? LIMIT 1', [params.id])
            if (res.affectedRows == 0) return ({code: -1, message: '撤销失败'})
            // 将审核人员的任务状态改下 并通知前端
            res = await mysqlUtil.execute('SELECT id, uid FROM tb_user_task WHERE typ = 19 AND relevenceId = ?', [params.id])
            for (let i = 0; i != res.length; i++) {
                await mysqlUtil.execute('UPDATE tb_user_task SET sta = 3 WHERE id = ? LIMIT 1', [res[i].id])
                // 通知前端
                io.in(res[i].uid + '-app').emit('TASK-REVERT', res[i].id)
                io.in(res[i].uid + '-web').emit('TASK-REVERT', res[i].id)
            }
            callback({code: 0, message: ''})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    bukaApplyRecord: async(io, socket, params, callback) => {

        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc

        // 小于的id
        let lessThan = params.lessThan || 0
        // 大于的id
        let greaterThan = params.greaterThan || 0
        // 每页的数量
        let pageCount = params.pageCount || 10

        try {
            let sql = ''
            let sqlParams = []
            if (lessThan) {
                sql = 'SELECT * FROM tb_user_buka_apply WHERE uid = ? AND id < ? ORDER BY id DESC LIMIT ?'
                sqlParams = [uid, lessThan, pageCount]
            } else if (greaterThan) {
                sql = 'SELECT * FROM tb_user_buka_apply WHERE uid = ? AND id > ? ORDER BY id DESC LIMIT ?'
                sqlParams = [uid, greaterThan, pageCount]
            } else {
                sql = 'SELECT * FROM tb_user_buka_apply WHERE uid = ? ORDER BY id DESC LIMIT ?'
                sqlParams = [uid, pageCount]
            }
            let res = await mysqlUtil.execute(sql, sqlParams)
            callback({code: 0, message: 'success', data: res})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    bukaApplyDetail: async(io, socket, params, callback) => {
        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc

        if (!params.id) return callback({code: -1, message: '参数错误'})

        try {
            let res = await mysqlUtil.execute('SELECT * FROM tb_user_buka_apply WHERE id = ? LIMIT 1', [params.id])
            if (res.length == 0) return callback({code: -1, message: '不存在'})
            callback({code: 0, message: '', data: res[0]})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    // 个人考勤明细
    personalKaoqinDetail: async (io, socket, params, callback) => {

        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc

        if (!params.month) return callback({code: -1, message: '参数缺失'})

        try {
            let monthStart = moment(params.month).startOf('month').format('YYYY-MM-DD')
            let monthEnd = moment(params.month).endOf('month').format('YYYY-MM-DD')
            let todayDate = moment().format('YYYY-MM-DD')
            let sql = `
                SELECT 
                    tb_user_daka.day,
                    tb_user_daka.shangbanTimeRes,
                    tb_user_daka.shangbanBukaSta,
                    tb_user_daka.shangbanPosRes,
                    tb_user_daka.xiabanTimeRes,
                    tb_user_daka.xiabanBukaSta,
                    tb_user_daka.xiabanPosRes,
                WHERE uid = ? AND day >= ? AND day <= ? AND day < ?
            `
            let res = await mysqlUtil.execute(sql, [uid, monthStart, monthEnd, todayDate])
            callback({code: 0, message: 'success', data: res})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    // 个人考勤统计
    personalKaoqinStatis: async (io, socket, params, callback) => {
        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc

        try {
            // 查询最近2个月的信息
            // 当月
            let currentMonthStatis = {
                year: moment().format('YYYY'),
                month: moment().format('MM'),
                shangbanweidaka: 0,
                xiabanweidaka: 0,
                chidao: 0,
                zaotui: 0,
                waiqin: 0,
                qingjia: 0,
                chuchai: 0,
                jiabancishu: 0,
                jiabanshichang: 0,
                buka: 0
            }
            // 考勤
            let res = await mysqlUtil.execute('SELECT * FROM tb_user_daka WHERE uid = ? AND day >= ? AND day <= ?', [uid, moment().startOf('month').format('YYYY-MM'), moment().endOf('month').format('YYYY-MM-DD')])
            for (let i = 0; i != res.length; i++) {

                // 只统计今天之前的
                if (moment(res[i].day).startOf('day').isSameOrAfter(moment().startOf('day'))) continue

                if (res[i].shangbanTime == -1) {
                    currentMonthStatis.shangbanweidaka++
                }
                if (res[i].xiabanTime == -1) {
                    currentMonthStatis.xiabanweidaka++
                }
                if (res[i].shangbanTimeRes != 0) {
                    currentMonthStatis.chidao++
                }
                if (res[i].xiabanTimeRes != 0) {
                    currentMonthStatis.zaotui++
                }
                if (res[i].shangbanBukaSta != 0) {
                    currentMonthStatis.buka++
                }
                if (res[i].xiabanBukaSta != 0) {
                    currentMonthStatis.buka++
                }
            }
            // 加班
            res = await mysqlUtil.execute('SELECT * FROM tb_user_jiaban_apply WHERE uid = ? AND startTime > ? AND startTime < ?', [uid, moment().startOf('month').valueOf(), moment().endOf('month').valueOf()])
            for (let i = 0; i != res.length; i++) {
                currentMonthStatis.jiabancishu++
                let deductionTimes = deductionTime(info.start, info.end) || []
                let deductionHours = 0
                for (let i = 0; i != deductionTimes.length; i++) {
                    let diff = moment(deductionTimes[i].end).valueOf() - moment(deductionTimes[i].start).valueOf()
                    let hour = Math.ceil(diff / 1000 / 60 / 60)
                    deductionHours += hour
                }
                let hours = (res[i].endTime - res[i].startTime) / 1000 / 60 / 60
                hours -= deductionHours
                currentMonthStatis.jiabanshichang = hours
            }
            // 出差
            res = await mysqlUtil.execute('SELECT * FROM tb_user_chuchai WHERE uid = ? AND day >= ? AND day <= ?', [uid, moment().startOf('month').format('YYYY-MM-DD'), moment().endOf('month').format('YYYY-MM-DD')])
            for (let i = 0; i != res.length; i++) {
                currentMonthStatis.chuchai++
            }
            // 请假
            res = await mysqlUtil.execute('SELECT * FROM tb_user_qingjia WHERE uid = ? AND day >= ? AND day <= ?', [uid, moment().startOf('month').format('YYYY-MM-DD'), moment().endOf('month').format('YYYY-MM-DD')])
            for (let i = 0; i != res.length; i++) {
                currentMonthStatis.qingjia++
            }

            // 上月
            let lastMonthStatis = {
                year: moment().add(-1, 'month').format('YYYY'),
                month: moment().add(-1, 'month').format('MM'),
                shangbanweidaka: 0,
                xiabanweidaka: 0,
                chidao: 0,
                zaotui: 0,
                waiqin: 0,
                qingjia: 0,
                chuchai: 0,
                jiabancishu: 0,
                jiabanshichang: 0,
                buka: 0
            }
            // 考勤
            res = await mysqlUtil.execute('SELECT * FROM tb_user_daka WHERE uid = ? AND day >= ? AND day <= ?', [uid, moment().add(-1, 'month').startOf('month').format('YYYY-MM'), moment().add(-1, 'month').endOf('month').format('YYYY-MM-DD')])
            for (let i = 0; i != res.length; i++) {
                if (res[i].shangbanTime == -1) {
                    lastMonthStatis.shangbanweidaka++
                }
                if (res[i].xiabanTime == -1) {
                    lastMonthStatis.xiabanweidaka++
                }
                if (res[i].shangbanTimeRes != 0) {
                    lastMonthStatis.chidao++
                }
                if (res[i].xiabanTimeRes != 0) {
                    lastMonthStatis.zaotui++
                }
                if (res[i].shangbanBukaSta != 0) {
                    lastMonthStatis.buka++
                }
                if (res[i].xiabanBukaSta != 0) {
                    lastMonthStatis.buka++
                }
            }
            // 加班
            res = await mysqlUtil.execute('SELECT * FROM tb_user_jiaban_apply WHERE uid = ? AND startTime > ? AND startTime < ?', [uid, moment().add(-1, 'month').startOf('month').valueOf(), moment().add(-1, 'month').endOf('month').valueOf()])
            for (let i = 0; i != res.length; i++) {
                lastMonthStatis.jiabancishu++

                let deductionTimes = deductionTime(info.start, info.end) || []
                let deductionHours = 0
                for (let i = 0; i != deductionTimes.length; i++) {
                    let diff = moment(deductionTimes[i].end).valueOf() - moment(deductionTimes[i].start).valueOf()
                    let hour = Math.ceil(diff / 1000 / 60 / 60)
                    deductionHours += hour
                }
                let hours = (res[i].endTime - res[i].startTime) / 1000 / 60 / 60
                hours -= deductionHours
                lastMonthStatis.jiabanshichang = hours
            }
            // 出差
            res = await mysqlUtil.execute('SELECT * FROM tb_user_chuchai WHERE uid = ? AND day >= ? AND day <= ?', [uid, moment().add(-1, 'month').startOf('month').format('YYYY-MM-DD'), moment().add(-1, 'month').endOf('month').format('YYYY-MM-DD')])
            for (let i = 0; i != res.length; i++) {
                lastMonthStatis.chuchai++
            }
            // 请假
            res = await mysqlUtil.execute('SELECT * FROM tb_user_qingjia WHERE uid = ? AND day >= ? AND day <= ?', [uid, moment().add(-1, 'month').startOf('month').format('YYYY-MM-DD'), moment().add(-1, 'month').endOf('month').format('YYYY-MM-DD')])
            for (let i = 0; i != res.length; i++) {
                lastMonthStatis.qingjia++
            }

            callback({code: 0, message: '', data: [currentMonthStatis, lastMonthStatis]})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    // 部门考勤统计
    groupKaoqinStatis: async (io, socket, params, callback) => {
        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc

        let data = []
        try {
            // 查询部门
            let sql = `
                SELECT
                    DISTINCT(tb_user.department) departId,
                    con_department.name departName
                FROM
                    tb_user
                INNER JOIN con_department ON tb_user.department = con_department.id
                WHERE tb_user.uid = ?
            `
            let res = await mysqlUtil.execute(sql, [uid])
            for (let i = 0; i != res.length; i++) {
                // 查询最近两个月的数据
                // 当月
                let currentMonthStatis = {
                    year: moment().format('YYYY'),
                    month: moment().format('MM'),
                    shangbanweidaka: 0,
                    shangbanweidakaDetail: {},
                    xiabanweidaka: 0,
                    xiabanweidakaDetail: {},
                    chidao: 0,
                    chidaoDetail: {},
                    zaotui: 0,
                    zaotuiDetail: {},
                    waiqin: 0,
                    waiqinDetail: {},
                    qingjia: 0,
                    qingjiaDetail: {},
                    chuchai: 0,
                    chuchaiDetail: {},
                    jiabancishu: 0,
                    jiabancishuDetail: {},
                    jiabanshichang: 0,
                    jiabanshichangDetail: {},
                    buka: 0,
                    bukaDetail: {},
                    waiqin: 0,
                    waiqinDetail: {}
                }
                
                // 查询部门人员
                let res1 = await mysqlUtil.execute('SELECT uid FROM tb_user WHERE department = ?', [res[i].departId])
                if (res1.length == 0) {
                    data.push({name: res[i].departName, statis: [currentMonthStatis, lastMonthStatis]})
                    continue
                }
                let uids = []
                for (let i = 0; i != res1.length; i++) {
                    uids.push(res1[i].uid)
                }
                // 考勤
                let sql = `
                    SELECT 
                        tb_user_daka.*,
                        tb_user.name
                    FROM 
                        tb_user_daka
                    INNER JOIN tb_user ON tb_user.uid = tb_user_daka.uid
                    WHERE tb_user_daka.uid IN (?) AND tb_user_daka.day >= ? AND tb_user_daka.day <= ?
                `
                let res2 = await mysqlUtil.execute(sql, [uids, moment().startOf('month').format('YYYY-MM'), moment().endOf('month').format('YYYY-MM-DD')])
                for (let i = 0; i != res2.length; i++) {

                    // 只统计今天之前的
                    // if (moment(res2[i].day).startOf('day').isSameOrAfter(moment().startOf('day'))) continue

                    // 上班未打卡
                    if (res2[i].shangbanTime == -1) {
                        currentMonthStatis.shangbanweidaka++
                        currentMonthStatis.shangbanweidakaDetail[res2[i].uid] = currentMonthStatis.shangbanweidakaDetail[res2[i].uid] || {name: res2[i].name, val: 0}
                        currentMonthStatis.shangbanweidakaDetail[res2[i].uid].val++
                    }
                    // 下班未打卡
                    if (res2[i].xiabanTime == -1) {
                        currentMonthStatis.xiabanweidaka++
                        currentMonthStatis.xiabanweidakaDetail[res2[i].uid] = currentMonthStatis.xiabanweidakaDetail[res2[i].uid] || {name: res2[i].name, val: 0}
                        currentMonthStatis.xiabanweidakaDetail[res2[i].uid].val++
                    }
                    // 迟到
                    if (res2[i].shangbanTimeRes != 0) {
                        currentMonthStatis.chidao++
                        currentMonthStatis.chidaoDetail[res2[i].uid] = currentMonthStatis.chidaoDetail[res2[i].uid] || {name: res2[i].name, val: 0}
                        currentMonthStatis.chidaoDetail[res2[i].uid].val++
                    }
                    // 早退
                    if (res2[i].xiabanTimeRes != 0) {
                        currentMonthStatis.zaotui++
                        currentMonthStatis.zaotuiDetail[res2[i].uid] = currentMonthStatis.zaotuiDetail[res2[i].uid] || {name: res2[i].name, val: 0}
                        currentMonthStatis.zaotuiDetail[res2[i].uid].val++
                    }
                    // 上班补卡
                    if (res2[i].shangbanBukaSta != 0) {
                        currentMonthStatis.buka++
                        currentMonthStatis.bukaDetail[res2[i].uid] = currentMonthStatis.bukaDetail[res2[i].uid] || {name: res2[i].name, val: 0}
                        currentMonthStatis.bukaDetail[res2[i].uid].val++
                    }
                    // 下班补卡
                    if (res2[i].xiabanBukaSta != 0) {
                        currentMonthStatis.buka++
                        currentMonthStatis.bukaDetail[res2[i].uid] = currentMonthStatis.bukaDetail[res2[i].uid] || {name: res2[i].name, val: 0}
                        currentMonthStatis.bukaDetail[res2[i].uid].val++
                    }
                    // 上班外勤
                    if (res2[i].shangbanWaiqin != 0) {
                        currentMonthStatis.waiqin++
                        currentMonthStatis.waiqinDetail[res2[i].uid] = currentMonthStatis.waiqinDetail[res2[i].uid] || {name: res2[i].name, val: 0}
                        currentMonthStatis.waiqinDetail[res2[i].uid].val++
                    }
                    // 下班外勤
                    if (res2[i].xiabanWaiqin != 0) {
                        currentMonthStatis.waiqin++
                        currentMonthStatis.waiqinDetail[res2[i].uid] = currentMonthStatis.waiqinDetail[res2[i].uid] || {name: res2[i].name, val: 0}
                        currentMonthStatis.waiqinDetail[res2[i].uid].val++
                    }
                    // 请假
                    if (res2[i].qingjiaInfo) {
                        let info = res2[i].qingjiaInfo
                        logger.debug('qingjia info', info)

                        let diff = moment(info.end).valueOf() - moment(info.start).valueOf()
                        logger.debug('qingjia info diff', diff)
                        let hour = Math.ceil(diff / 1000 / 60 / 60)
                        
                        currentMonthStatis.qingjia += hour
                        currentMonthStatis.qingjiaDetail[res2[i].uid] = currentMonthStatis.qingjiaDetail[res2[i].uid] || {name: res2[i].name, val: 0}
                        currentMonthStatis.qingjiaDetail[res2[i].uid].val += hour
                    }
                    // 加班
                    if (res2[i].jiabanInfo) {
                        let info = res2[i].jiabanInfo

                        let deductionTimes = deductionTime(info.start, info.end) || []
                        let deductionHours = 0
                        for (let i = 0; i != deductionTimes.length; i++) {
                            let diff = moment(deductionTimes[i].end).valueOf() - moment(deductionTimes[i].start).valueOf()
                            let hour = Math.ceil(diff / 1000 / 60 / 60)
                            deductionHours += hour
                        }

                        let diff = moment(info.end).valueOf() - moment(info.start).valueOf()
                        let hour = Math.floor(diff / 1000 / 60 / 60)
                        hour -= deductionHours
                        currentMonthStatis.jiabanshichang += hour
                        currentMonthStatis.jiabanshichangDetail[res2[i].uid] = currentMonthStatis.jiabanshichangDetail[res2[i].uid] || {name: res2[i].name, val: 0}
                        currentMonthStatis.jiabanshichangDetail[res2[i].uid].val += hour

                        currentMonthStatis.jiabancishu++
                        currentMonthStatis.jiabancishuDetail[res2[i].uid] = currentMonthStatis.jiabancishuDetail[res2[i].uid] || {name: res2[i].name, val: 0}
                        currentMonthStatis.jiabancishuDetail[res2[i].uid].val++
                    }

                    // 出差
                    if (res2[i].chuchaiInfo) {
                        currentMonthStatis.chuchai++
                        currentMonthStatis.chuchaiDetail[res2[i].uid] = currentMonthStatis.chuchaiDetail[res2[i].uid] || {name: res2[i].name, val: 0}
                        currentMonthStatis.chuchaiDetail[res2[i].uid].val++
                    }
                }

                // 上月
                let lastMonthStatis = {
                    year: moment().add(-1, 'month').format('YYYY'),
                    month: moment().add(-1, 'month').format('MM'),
                    shangbanweidaka: 0,
                    shangbanweidakaDetail: {},
                    xiabanweidaka: 0,
                    xiabanweidakaDetail: {},
                    chidao: 0,
                    chidaoDetail: {},
                    zaotui: 0,
                    zaotuiDetail: {},
                    waiqin: 0,
                    waiqinDetail: {},
                    qingjia: 0,
                    qingjiaDetail: {},
                    chuchai: 0,
                    chuchaiDetail: {},
                    jiabancishu: 0,
                    jiabancishuDetail: {},
                    jiabanshichang: 0,
                    jiabanshichangDetail: {},
                    buka: 0,
                    bukaDetail: {},
                    waiqin: 0,
                    waiqinDetail: {}
                }
                // 考勤
                let res6 = await mysqlUtil.execute(sql, [uids, moment().add(-1, 'month').startOf('month').format('YYYY-MM'), moment().add(-1, 'month').endOf('month').format('YYYY-MM-DD')])
                for (let i = 0; i != res6.length; i++) {
                    if (res6[i].shangbanTime == -1) {
                        lastMonthStatis.shangbanweidaka++
                        lastMonthStatis.shangbanweidakaDetail[res6[i].uid] = lastMonthStatis.shangbanweidakaDetail[res6[i].uid] || {name: res6[i].name, val: 0}
                        lastMonthStatis.shangbanweidakaDetail[res6[i].uid].val++
                    }
                    if (res6[i].xiabanTime == -1) {
                        lastMonthStatis.xiabanweidaka++
                        lastMonthStatis.xiabanweidakaDetail[res6[i].uid] = lastMonthStatis.xiabanweidakaDetail[res6[i].uid] || {name: res6[i].name, val: 0}
                        lastMonthStatis.xiabanweidakaDetail[res6[i].uid].val++
                    }
                    if (res6[i].shangbanTimeRes != 0) {
                        lastMonthStatis.chidao++
                        lastMonthStatis.chidaoDetail[res6[i].uid] = lastMonthStatis.chidaoDetail[res6[i].uid] || {name: res6[i].name, val: 0}
                        lastMonthStatis.chidaoDetail[res6[i].uid].val++
                    }
                    if (res6[i].xiabanTimeRes != 0) {
                        lastMonthStatis.zaotui++
                        lastMonthStatis.zaotuiDetail[res6[i].uid] = lastMonthStatis.zaotuiDetail[res6[i].uid] || {name: res6[i].name, val: 0}
                        lastMonthStatis.zaotuiDetail[res6[i].uid].val++
                    }
                    if (res6[i].shangbanBukaSta != 0) {
                        lastMonthStatis.buka++
                        lastMonthStatis.bukaDetail[res6[i].uid] = lastMonthStatis.bukaDetail[res6[i].uid] || {name: res6[i].name, val: 0}
                        lastMonthStatis.bukaDetail[res6[i].uid].val++
                    }
                    if (res6[i].xiabanBukaSta != 0) {
                        lastMonthStatis.buka++
                        lastMonthStatis.bukaDetail[res6[i].uid] = lastMonthStatis.bukaDetail[res6[i].uid] || {name: res6[i].name, val: 0}
                        lastMonthStatis.bukaDetail[res6[i].uid].val++
                    }
                    // 上班外勤
                    if (res6[i].shangbanWaiqin != 0) {
                        lastMonthStatis.waiqin++
                        lastMonthStatis.waiqinDetail[res6[i].uid] = lastMonthStatis.waiqinDetail[res6[i].uid] || {name: res6[i].name, val: 0}
                        lastMonthStatis.waiqinDetail[res6[i].uid].val++
                    }
                    // 下班外勤
                    if (res6[i].xiabanWaiqin != 0) {
                        lastMonthStatis.waiqin++
                        lastMonthStatis.waiqinDetail[res6[i].uid] = lastMonthStatis.waiqinDetail[res6[i].uid] || {name: res6[i].name, val: 0}
                        lastMonthStatis.waiqinDetail[res6[i].uid].val++
                    }
                    if (res6[i].qingjiaInfo) {
                        let info = res6[i].qingjiaInfo
                        let diff = moment(info.end).valueOf() - moment(info.start).valueOf()
                        let hour = Math.ceil(diff / 1000 / 60 / 60)
                        lastMonthStatis.qingjia += hour
                        lastMonthStatis.qingjiaDetail[res6[i].uid] = lastMonthStatis.qingjiaDetail[res6[i].uid] || {name: res6[i].name, val: 0}
                        lastMonthStatis.qingjiaDetail[res6[i].uid].val += hour
                    }
                    if (res6[i].jiabanInfo) {
                        let info = res6[i].jiabanInfo

                        let deductionTimes = deductionTime(info.start, info.end) || []
                        let deductionHours = 0
                        for (let i = 0; i != deductionTimes.length; i++) {
                            let diff = moment(deductionTimes[i].end).valueOf() - moment(deductionTimes[i].start).valueOf()
                            let hour = Math.ceil(diff / 1000 / 60 / 60)
                            deductionHours += hour
                        }

                        let diff = moment(info.end).valueOf() - moment(info.start).valueOf()
                        let hour = Math.floor(diff / 1000 / 60 / 60)
                        hour -= deductionHours
                        lastMonthStatis.jiabanshichang += hour
                        lastMonthStatis.jiabanshichangDetail[res6[i].uid] = lastMonthStatis.jiabanshichangDetail[res6[i].uid] || {name: res6[i].name, val: 0}
                        lastMonthStatis.jiabanshichangDetail[res6[i].uid].val += hour

                        lastMonthStatis.jiabancishu++
                        lastMonthStatis.jiabancishuDetail[res6[i].uid] = lastMonthStatis.jiabancishuDetail[res6[i].uid] || {name: res6[i].name, val: 0}
                        lastMonthStatis.jiabancishuDetail[res6[i].uid].val++
                    }

                    if (res6[i].chuchaiInfo) {
                        lastMonthStatis.chuchai++
                        lastMonthStatis.chuchaiDetail[res6[i].uid] = lastMonthStatis.chuchaiDetail[res6[i].uid] || {name: res6[i].name, val: 0}
                        lastMonthStatis.chuchaiDetail[res6[i].uid].val++
                    }
                }

                data.push({name: res[i].departName, id: res[i].departId, statis: [currentMonthStatis, lastMonthStatis]})
            }

            callback({code: 0, message: '', data})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    // 导出部门统计
    exportGroupStatis: async (io, socket, params, callback) => {
        let uid = socket.uid
        callback = callback || commonModule.emptyFunc
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        if (!params.year || !params.month || !params.departId) return callback({code: -1, message: '参数缺失'})

        try {
            let m = moment()
            m.year(params.year)
            m.month(params.month - 1)

            let startStr = m.startOf('month').format('YYYY-MM-DD')
            let endStr = m.endOf('month').format('YYYY-MM-DD')
            if (moment(endStr).isAfter(moment())) {
                endStr = moment().format('YYYY-MM-DD')
            }

            // 查询考勤设置
            let sql = 'SELECT val FROM con_system_param WHERE name = "KAOQIN_SHEZHI" LIMIT 1'
            let res = await mysqlUtil.execute(sql)
            if (res.length == 0) {
                return callback({code: -1, message: '请先进行考勤设置'})
            }

            kaoqinSetting = JSON.parse(res[0].val)
            let shangbanTimeStr = kaoqinSetting.shangbanTime + ':00'
            let xiabanTimeStr = kaoqinSetting.xiabanTime + ':00'

            sql = `
            SELECT
                tb_user_daka.*,
                tb_user.name userName,
                con_department.name departName
            FROM
                tb_user_daka
            LEFT JOIN tb_user ON tb_user.uid = tb_user_daka.uid
            LEFT JOIN con_department ON tb_user.department = con_department.id
            WHERE
                tb_user.department = ?
                AND tb_user_daka.day >= ? AND tb_user_daka.day <= ?
            `
            res = await mysqlUtil.execute(sql, [params.departId, startStr, endStr])

            let statis = {}
            let departName = ''
            for (let i = 0; i != res.length; i++) {
                let dakaDetail = res[i]

                departName = dakaDetail.departName

                let shangbanTimeStamp = moment(moment(dakaDetail.day).format('YYYY-MM-DD ') + shangbanTimeStr).valueOf()
                let xiabanTimeStamp = moment(moment(dakaDetail.day).format('YYYY-MM-DD ') + xiabanTimeStr).valueOf()

                if (!statis[dakaDetail.uid]) {
                    statis[dakaDetail.uid] = {
                        uid: dakaDetail.uid,
                        name: dakaDetail.userName,
                        departName: dakaDetail.departName,
                        chuqintianshu: 0, // 出勤天数
                        xiuxitianshu: 0, // 休息天数
                        gongzuoshichang: 0, // 工作时长
                        shangbanweidaka: 0, // 上班未打卡
                        xiabanweidaka: 0, // 下班未打卡
                        chidaocishu: 0, // 迟到次数
                        chidaoshichang: 0, // 迟到市场
                        zaotuicishu: 0, // 早退次数
                        zaotuishichang: 0, // 早退时长
                        waiqin: 0, // 外勤次数
                        qingjiacishu: 0, // 请假次数
                        qingjiashichang: 0, // 请假时长
                        chuchaicishu: 0, // 出差次数
                        chuchaishichang: 0, // 出差时长
                        jiabancishu: 0, // 加班次数
                        jiabanshichang: 0, // 加班时长
                        buka: 0, // 补卡次数
                    }

                    if (dakaDetail.sta == null) continue

                    // 出勤天数 当天上班或者值班 有上班打卡或者下班打卡记录
                    if (dakaDetail.sta.id == 1 || dakaDetail.sta.id == 2) {
                        if (dakaDetail.shangbanTime != -1 || dakaDetail.xiabanTime == -1) {
                            statis[dakaDetail.uid].chuqintianshu++
                        }
                    }

                    // 休息天数
                    if (dakaDetail.sta.id == 0) {
                        statis[dakaDetail.uid].xiuxitianshu++
                    }

                    // 工作时长 如果有打卡记录
                    if (dakaDetail.sta.id == 1 || dakaDetail.sta.id == 2) {
                        if (dakaDetail.shangbanTime != -1 || dakaDetail.xiabanTime != -1) {
                            // 如果没有上班打卡记录 则上班时间就是规定的时间
                            if (dakaDetail.shangbanTime == -1) dakaDetail.shangbanTime = shangbanTimeStamp
                            if (dakaDetail.xiabanTime == -1) dakaDetail.xiabanTime = xiabanTimeStamp
    
                            statis[dakaDetail.uid].gongzuoshichang += (dakaDetail.xiabanTime - dakaDetail.shangbanTime)
                        }
                    }

                    // 上班未打卡
                    if (dakaDetail.sta.id == 1 || dakaDetail.sta.id == 2) {
                        if (dakaDetail.shangbanTime == -1) {
                            statis[dakaDetail.uid].shangbanweidaka++
                        }
                    }

                    // 下班未打卡
                    if (dakaDetail.sta.id == 1 || dakaDetail.sta.id == 2) {
                        if (dakaDetail.xiabanTime == -1) {
                            statis[dakaDetail.uid].xiabanweidaka++
                        }
                    }

                    // 迟到
                    if (dakaDetail.sta.id == 1 || dakaDetail.sta.id == 2) {
                        if (dakaDetail.shangbanTimeRes > 0) {
                            statis[dakaDetail.uid].chidaocishu++
                            statis[dakaDetail.uid].chidaoshichang += (dakaDetail.shangbanTime - shangbanTimeStamp)
                        }
                    }

                    // 早退
                    if (dakaDetail.sta.id == 1 || dakaDetail.sta.id == 2) {
                        if (dakaDetail.xiabanTimeRes > 0) {
                            statis[dakaDetail.uid].zaotuicishu++
                            statis[dakaDetail.uid].zaotuishichang += (xiabanTimeStamp - dakaDetail.xiabanTime)
                        }
                    }

                    // 外勤
                    if (dakaDetail.sta.id == 1 || dakaDetail.sta.id == 2) {
                        if (dakaDetail.shangbanWaiqin) {
                            statis[dakaDetail.uid].waiqin++
                        }
                        if (dakaDetail.xiabanWaiqin) {
                            statis[dakaDetail.uid].waiqin++
                        }
                    }

                    // 请假
                    if (dakaDetail.qingjiaInfo) {
                        statis[dakaDetail.uid].qingjiacishu++
                        statis[dakaDetail.uid].qingjiashichang += (moment(dakaDetail.qingjiaInfo.end).valueOf() - moment(dakaDetail.qingjiaInfo.start).valueOf())
                    }

                    // 出差
                    if (dakaDetail.chuchaiInfo) {
                        statis[dakaDetail.uid].chuchaicishu++
                        // statis[dakaDetail.uid].chuchaishichang += (moment(info.end).valueOf() - moment(info.end).valueOf())
                    }

                    // 加班
                    if (dakaDetail.jiabanInfo) {
                        statis[dakaDetail.uid].jiabancishu++
                        statis[dakaDetail.uid].jiabanshichang += (moment(dakaDetail.jiabanInfo.end).valueOf() - moment(dakaDetail.jiabanInfo.start).valueOf())
                    }

                    // 补卡
                    if (dakaDetail.shangbanBukaSta > 0) {
                        statis[dakaDetail.uid].buka++
                    }
                    if (dakaDetail.xiabanBukaSta > 0) {
                        statis[dakaDetail.uid].buka++
                    }
                }
            }

            logger.debug(statis)
            let data = [['姓名', '部门', '工号', '出勤天数', '休息天数', '工作时长', '上班未打卡', '下班未打卡', '迟到次数', '迟到时长', '早退次数', '早退时长', '外勤次数', '请假时长', '出差天数', '加班次数', '加班时长', '补卡次数']]
            for (let uid in statis) {
                data.push([
                    statis[uid].name,
                    statis[uid].departName,
                    uid,
                    statis[uid].chuqintianshu,
                    statis[uid].xiuxitianshu,
                    Math.floor(statis[uid].gongzuoshichang / 1000 / 60 / 60) + '小时',
                    statis[uid].shangbanweidaka,
                    statis[uid].xiabanweidaka,
                    statis[uid].chidaocishu,
                    Math.ceil(statis[uid].chidaoshichang / 1000 / 60) + '分钟',
                    statis[uid].zaotuicishu,
                    Math.ceil(statis[uid].zaotuishichang / 1000 / 60) + '分钟',
                    statis[uid].waiqin,
                    Math.ceil(statis[uid].qingjiashichang / 1000 / 60 / 60),
                    statis[uid].chuchaicishu,
                    statis[uid].jiabancishu,
                    Math.floor(statis[uid].jiabanshichang / 1000 / 60) + '分钟',
                    statis[uid].buka
                ])
            }

            let buffer = xlsx.build([{name: 'sheet1', data: data}])

            let fileName = `${params.year}年${params.month}月-${departName}-考勤汇总-${moment().format('YYYYMMDDHHmmss')}`
            fs.writeFileSync(path.resolve(__dirname, '../static/' + fileName + '.xlsx'), buffer)

            callback({code: 0, message: '', data: config.baseUrl + fileName + '.xlsx'})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    // 获取考勤设置
    getKaoqinSetting: async (io, socket, params, callback) => {

        let uid = socket.uid
        callback = callback || commonModule.emptyFunc
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        try {
            let kaoqinSetting = {
                jiabanMinMinutes: 60, // 最短加班时长
                bukaMaxTimes: 5, // 每月最多补卡次数
                bukaDays: 7, // 几天内可以补卡
                chidao1Minutes: 10, // 一般迟到
                chidao2Minutes: 30, // 严重迟到
                chidao3Minutes: 60, // 迟到导致旷工
                zaotui1Minutes: 10, // 一般早退
                zaotui2Minutes: 30, // 严重早退
                zaotui3Minutes: 60, // 早退导致旷工
                waiqinAudit: 1, // 外勤是否需要审批
                chuchaiDaka: 1, // 出差是否需要打卡
                shangbanTime: '09:00', // 上班时间
                xiabanTime: '17:00', // 下班时间
                dakaFanwei: 500, // 打卡范围
                dakaLongitude: 118.692058, // 打卡经度
                dakaLatitude: 32.163314, // 打卡纬度
                dakaAddress: '盐城珍禽保护区大门', // 打卡地址
            }
            let sql = 'SELECT val FROM con_system_param WHERE name = "KAOQIN_SHEZHI" LIMIT 1'
            let res = await mysqlUtil.execute(sql)
            if (res.length == 0) {
                await mysqlUtil.execute('INSERT INTO con_system_param SET ?', [{name: 'KAOQIN_SHEZHI', description: '考勤设置', val: JSON.stringify(kaoqinSetting)}])
            } else {
                kaoqinSetting = JSON.parse(res[0].val)
            }
            callback({code: 0, message: 'success', data: kaoqinSetting})
        } catch (err) {
            logger.error(err)
            callback({code:-1, message: 'system error'})
        }
    },

    // 更新考勤设置
    updateKaoqinSetting: async (io, socket, params, callback) => {

        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc

        let kaoqinSetting = {
            jiabanMinMinutes: params.jiabanMinMinutes || 0, // 最短加班时长
            bukaMaxTimes: params.bukaMaxTimes || 0, // 每月最多补卡次数
            bukaDays: params.bukaDays || 0, // 几天内可以补卡
            chidao1Minutes: params.chidao1Minutes || 0, // 一般迟到
            chidao2Minutes: params.chidao2Minutes || 0, // 严重迟到
            chidao3Minutes: params.chidao3Minutes || 0, // 迟到导致旷工
            zaotui1Minutes: params.zaotui1Minutes || 0, // 一般早退
            zaotui2Minutes: params.zaotui2Minutes || 0, // 严重早退
            zaotui3Minutes: params.zaotui3Minutes || 0, // 早退导致旷工
            waiqinAudit: params.waiqinAudit || 0, // 外勤是否需要审批
            chuchaiDaka: params.chuchaiDaka || 0, // 出差是否需要打卡
            shangbanTime: params.shangbanTime || '09:00', // 上班时间
            xiabanTime: params.xiabanHour || '17:00', // 下班时间
            dakaFanwei: params.dakaFanwei || 500, // 打卡范围
            dakaLongitude: params.dakaLongitude || 0, // 打卡经度
            dakaLatitude: params.dakaLatitude || 0, // 打卡纬度
            dakaAddress: params.dakaAddress || '盐城珍禽保护区大门', // 打卡地址
        }

        try {
            let sql = 'INSERT INTO con_system_param SET ? ON DUPLICATE KEY UPDATE ?'
            await mysqlUtil.execute(sql, [{name: 'KAOQIN_SHEZHI', val: JSON.stringify(kaoqinSetting), description: '考勤设置'}, {val: JSON.stringify(kaoqinSetting)}])
            callback({code: 0, message: 'success'})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    // 请假申请
    vacationApply: async (io, socket, params, callback) => {

        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc

        logger.debug('vacation apply', params)

        if (params.pics && !Array.isArray(params.pics)) return callback({code: -1, message: '参数错误'})

        let auditorsObj = {}
        for (let i = 0; i != params.auditors.length; i++) {
            auditorsObj['auditor' + i] = { uid: params.auditors[i].uid, stepName: params.auditors[i].stepName, sta: 0 }
        }

        try {
            let res = await mysqlUtil.execute('SELECT name FROM tb_user WHERE uid = ? LIMIT 1', [uid])
            if (res.length == 0) return callback({code: -1, message: '玩家不存在'})
            let name = res[0].name

            let sql = 'INSERT INTO tb_user_vacation_apply SET ?'
            let sqlParams = [{
                uid,
                sta: 0,
                auditSta: 1,
                leixing: params.leixing,
                startDate: params.startDate,
                endDate: params.endDate,
                days: params.days || 0,
                detail: JSON.stringify(params.detail || []),
                reason: params.reason,
                outYancheng: params.outYancheng,
                auditors: JSON.stringify(auditorsObj),
                pics: JSON.stringify(params.pics || []),
                attaches: JSON.stringify(params.attaches || []),
                createTime: Date.now()
            }]
            res = await mysqlUtil.execute(sql, sqlParams)

            let applyId = res.insertId

            if ((params.auditors || []).length > 0) {
                // 为审核人员添加任务
                await mysqlUtil.execute('INSERT INTO tb_user_task SET ?', [{
                    uid: params.auditors[0].uid,
                    typ: 0,
                    relevenceId: applyId,
                    pos: 0,
                    title: '请假申请',
                    content: name + '的请假申请需要您的审批',
                    sta: 0,
                    createTime: Date.now()
                }])
                io.in(params.auditors[0].uid + '-app').emit('TASK-CHANGE')
                io.in(params.auditors[0].uid + '-app').emit('TASK-NEW')
                io.in(params.auditors[0].uid + '-web').emit('TASK-CHANGE')
                io.in(params.auditors[0].uid + '-web').emit('TASK-NEW')
                util.sendReminderSmsToUser(params.auditors[0].uid, '请假申请', '待审批')
            }

            callback({code: 0, message: 'success'})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    // 撤销请假申请
    revertVacationApply: async (io, socket, params, callback) => {
        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc
        if (!params.id) return callback({code: -1, message: '参数缺失'})

        try {
            let res = await mysqlUtil.execute('UPDATE tb_user_vacation_apply SET chexiao = 1 WHERE id = ? LIMIT 1', [params.id])
            if (res.affectedRows == 0) return ({code: -1, message: '撤销失败'})
            // 将审核人员的任务状态改下 并通知前端
            res = await mysqlUtil.execute('SELECT id, uid FROM tb_user_task WHERE typ = 0 AND relevenceId = ?', [params.id])
            for (let i = 0; i != res.length; i++) {
                await mysqlUtil.execute('UPDATE tb_user_task SET sta = 3 WHERE id = ? LIMIT 1', [res[i].id])
                // 通知前端
                io.in(res[i].uid + '-app').emit('TASK-REVERT', res[i].id)
                io.in(res[i].uid + '-web').emit('TASK-REVERT', res[i].id)
            }
            callback({code: 0, message: ''})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    // 销假
    xiaojia: async (io, socket, params, callback) => {
        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc
        if (!params.id) return callback({code: -1, message: '参数缺失'})

        try {

            let res = await mysqlUtil.execute('SELECT name FROM tb_user WHERE uid = ? LIMIT 1', [uid])
            let myName = res[0].name

            res = await mysqlUtil.execute('SELECT * FROM tb_user_vacation_apply WHERE id = ? LIMIT 1', [params.id])
            if (res.length == 0) return callback({code: -1, message: '不存在'})
            if (res[0].xiaojia == 1) return callback({code: 0, message: 'success'})

            let lingdao = await getLingdao(uid)
            if (!lingdao.zhijie_lingdao) return callback({code: -1, message: '无直接领导'})

            await mysqlUtil.execute('UPDATE tb_user_vacation_apply SET xiaojia = 1 WHERE id = ? LIMIT 1', [params.id])

            // 发送通知给直接领导
            await mysqlUtil.execute('INSERT INTO tb_user_notification SET ?', [{
                uid: lingdao.zhijie_lingdao, // 直接领导
                typ: 0,
                title: '销假',
                content: myName + '假期结束，来上班了',
                sta: 0,
                resTyp: 3,
                relevenceId: params.id,
                createTime: Date.now()
            }])

            callback({code: 0, message: ''})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    // 请假记录
    vacationApplyRecord: async(io, socket, params, callback) => {
        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc

        // 小于的id
        let lessThan = params.lessThan || 0
        // 大于的id
        let greaterThan = params.greaterThan || 0
        // 每页的数量
        let pageCount = params.pageCount || 10

        try {
            let sql = ''
            let sqlParams = []
            if (lessThan) {
                sql = 'SELECT * FROM tb_user_vacation_apply WHERE uid = ? AND id < ? ORDER BY id DESC LIMIT ?'
                sqlParams = [uid, lessThan, pageCount]
            } else if (greaterThan) {
                sql = 'SELECT * FROM tb_user_vacation_apply WHERE uid = ? AND id > ? ORDER BY id DESC LIMIT ?'
                sqlParams = [uid, greaterThan, pageCount]
            } else {
                sql = 'SELECT * FROM tb_user_vacation_apply WHERE uid = ? ORDER BY id DESC LIMIT ?'
                sqlParams = [uid, pageCount]
            }
            let res = await mysqlUtil.execute(sql, sqlParams)
            callback({code: 0, message: 'success', data: res})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    vacationApplyRecordWeb: async(io, socket, params, callback) => {

        let uid = socket.uid
        callback = callback || commonModule.emptyFunc
        if (!uid) return callback({code: 9999, message: '请重新登录'})


        let pageIndex = parseInt(params.pageIndex || 1);
        let pageCount = parseInt(params.pageCount || 10);

        try {
            let res = await mysqlUtil.execute('SELECT COUNT(1) cnt FROM tb_user_vacation_apply WHERE uid = ?', [uid])
            let count = res[0].cnt
            let sql = 'SELECT * FROM tb_user_vacation_apply WHERE uid = ? ORDER BY id DESC LIMIT ?, ?'
            let params = [uid, (pageIndex - 1) * pageCount, pageCount]
            res = await mysqlUtil.execute(sql, params)
            callback({code: 0, message: 'success', data: {list: res, count}})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    vacationApplyDetail: async(io, socket, params, callback) => {
        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc

        if (!params.id) return callback({code: -1, message: '参数错误'})

        try {
            let res = await mysqlUtil.execute('SELECT * FROM tb_user_vacation_apply WHERE id = ? LIMIT 1', [params.id])
            if (res.length == 0) return callback({code: -1, message: '不存在'})
            let applyInfo = res[0]
            applyInfo.auditors = applyInfo.auditors || []
            for (let key in applyInfo.auditors) {
                let sql = `
                    SELECT
                        con_position.name
                    FROM
                        tb_user
                    INNER JOIN con_position ON con_position.id = tb_user.position
                    WHERE tb_user.uid = ?
                    LIMIT 1
                `
                let r = await mysqlUtil.execute(sql, [applyInfo.auditors[key].uid])
                if (r.length > 0) {
                    applyInfo.auditors[key].positionName = r[0].name
                }
            }
            callback({code: 0, message: '', data: applyInfo})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    // 同意请假申请
    aggreeVacationApply: async (io, socket, params, callback) => {

        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc

        if (!params.id || !params.pos) return callback({code: -1, message: '参数缺失'})

        if (params.pos != 1 && params.pos != 2 && params.pos != 3 && params.pos != 4) return callback({code: -1, message: '参数错误'})

        try {
            let sql = `SELECT * FROM tb_user_vacation_apply WHERE id = ? AND auditor${params.pos} = ? LIMIT 1`
            let res = await mysqlUtil(sql, [params.id, uid])
            if (res.length == 0) return callback({code: -1, message: '请假申请不存在 '})
            if (res[0].sta != 0) return callback({code: -1, message: '请假单已经处理，不可重复处理'})
            sql = `UPDATE tb_user_vacation_apply SET auditor${params.pos}Sta = 1, auditor${params.pos}Time = ? WHERE id = ? LIMIT 1`
            await mysqlUtil.execute(sql, [Date.now(), params.id])

            callback({code: 0, message: 'success'})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    // 拒绝请假申请
    rejectVacationApply: async (io, socket, params, callback) => {

        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc

        if (!params.id || !params.pos) return callback({code: -1, message: '参数缺失'})

        if (params.pos != 1 && params.pos != 2 && params.pos != 3 && params.pos != 4) return callback({code: -1, message: '参数错误'})

        try {
            let sql = `SELECT * FROM tb_user_vacation_apply WHERE id = ? AND auditor${params.pos} = ? LIMIT 1`
            let res = await mysqlUtil(sql, [params.id, uid])
            if (res.length == 0) return callback({code: -1, message: '请假申请不存在 '})
            if (res[0].sta != 0) return callback({code: -1, message: '请假单已经处理，不可重复处理'})
            sql = `UPDATE tb_user_vacation_apply SET auditor${params.pos}Sta = 2, auditor${params.pos}Time = ? WHERE id = ? LIMIT 1`
            await mysqlUtil.execute(sql, [Date.now(), params.id])

            callback({code: 0, message: 'success'})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    // 出差申请
    chuchaiApply: async (io, socket, params, callback) => {

        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc

        logger.debug('chuchai apply', params)

        let auditorsObj = {}
        for (let i = 0; i != params.auditors.length; i++) {
            auditorsObj['auditor' + i] = { uid: params.auditors[i].uid, stepName: params.auditors[i].stepName, sta: 0 }
        }

        try {
            let res = await mysqlUtil.execute('SELECT name FROM tb_user WHERE uid = ? LIMIT 1', [uid])
            if (res.length == 0) return callback({code: -1, message: '玩家不存在'})
            let myName = res[0].name

            let sql = 'INSERT INTO tb_user_chuchai_apply SET ?'
            let sqlParams = [{
                uid,
                sta: 0,
                auditSta: 1,
                chuchairen: JSON.stringify(params.chuchairen || []),
                department: params.department,
                startDate: params.startDate,
                endDate: params.endDate,
                reason: params.reason,
                chufa: params.chufa,
                destination: params.destination,
                days: params.days || 0,
                jiaotong: params.jiaotong ,
                auditors: JSON.stringify(auditorsObj),
                attaches: JSON.stringify(params.attaches || []),
                bao: JSON.stringify(params.bao || []),
                createTime: Date.now()
            }]
            res = await mysqlUtil.execute(sql, sqlParams)

            let applyId = res.insertId

            // 为审核人员添加任务
            await mysqlUtil.execute('INSERT INTO tb_user_task SET ?', [{
                uid: params.auditors[0].uid,
                typ: 1,
                relevenceId: applyId,
                pos: 0,
                title: '出差申请',
                content: myName + '的出差申请需要您的审批',
                sta: 0,
                createTime: Date.now()
            }])
            io.in(params.auditors[0].uid + '-app').emit('TASK-CHANGE')
            io.in(params.auditors[0].uid + '-app').emit('TASK-NEW')
            io.in(params.auditors[0].uid + '-web').emit('TASK-CHANGE')
            io.in(params.auditors[0].uid + '-web').emit('TASK-NEW')
            util.sendReminderSmsToUser(params.auditors[0].uid, '出差申请', '待审批')

            callback({code: 0, message: 'success'})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    // 撤销出差申请
    revertChuchaiApply: async (io, socket, params, callback) => {
        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc
        if (!params.id) return callback({code: -1, message: '参数缺失'})

        try {
            let res = await mysqlUtil.execute('UPDATE tb_user_chuchai_apply SET chexiao = 1 WHERE id = ? LIMIT 1', [params.id])
            if (res.affectedRows == 0) return ({code: -1, message: '撤销失败'})
            // 将审核人员的任务状态改下 并通知前端
            res = await mysqlUtil.execute('SELECT id, uid FROM tb_user_task WHERE typ = 1 AND relevenceId = ?', [params.id])
            for (let i = 0; i != res.length; i++) {
                await mysqlUtil.execute('UPDATE tb_user_task SET sta = 3 WHERE id = ? LIMIT 1', [res[i].id])
                // 通知前端
                io.in(res[i].uid + '-app').emit('TASK-REVERT', res[i].id)
                io.in(res[i].uid + '-web').emit('TASK-REVERT', res[i].id)
            }
            callback({code: 0, message: ''})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    // 出差记录
    chuchaiApplyRecord: async(io, socket, params, callback) => {
        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc

        // 小于的id
        let lessThan = params.lessThan || 0
        // 大于的id
        let greaterThan = params.greaterThan || 0
        // 每页的数量
        let pageCount = params.pageCount || 10

        try {
            let sql = ''
            let sqlParams = []
            if (lessThan) {
                sql = 'SELECT * FROM tb_user_chuchai_apply WHERE uid = ? AND id < ? ORDER BY id DESC LIMIT ?'
                sqlParams = [uid, lessThan, pageCount]
            } else if (greaterThan) {
                sql = 'SELECT * FROM tb_user_chuchai_apply WHERE uid = ? AND id > ? ORDER BY id DESC LIMIT ?'
                sqlParams = [uid, greaterThan, pageCount]
            } else {
                sql = 'SELECT * FROM tb_user_chuchai_apply WHERE uid = ? ORDER BY id DESC LIMIT ?'
                sqlParams = [uid, pageCount]
            }
            let res = await mysqlUtil.execute(sql, sqlParams)
            callback({code: 0, message: 'success', data: res})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    chuchaiApplyRecordWeb: async(io, socket, params, callback) => {

        let uid = socket.uid
        callback = callback || commonModule.emptyFunc
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        let pageIndex = parseInt(params.pageIndex || 1);
        let pageCount = parseInt(params.pageCount || 10);

        try {
            let res = await mysqlUtil.execute('SELECT COUNT(1) cnt FROM tb_user_chuchai_apply WHERE uid = ?', [uid])
            let count = res[0].cnt
            let sql = 'SELECT * FROM tb_user_chuchai_apply WHERE uid = ? ORDER BY id DESC LIMIT ?, ?'
            let params = [uid, (pageIndex - 1) * pageCount, pageCount]
            res = await mysqlUtil.execute(sql, params)
            callback({code: 0, message: 'success', data: {list: res, count}})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    chuchaiApplyDetail: async(io, socket, params, callback) => {
        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc

        if (!params.id) return callback({code: -1, message: '参数错误'})

        try {
            let res = await mysqlUtil.execute('SELECT * FROM tb_user_chuchai_apply WHERE id = ? LIMIT 1', [params.id])
            if (res.length == 0) return callback({code: -1, message: '不存在'})
            let applyInfo = res[0]
            applyInfo.auditors = applyInfo.auditors || []
            for (let key in applyInfo.auditors) {
                let sql = `
                    SELECT
                        con_position.name
                    FROM
                        tb_user
                    INNER JOIN con_position ON con_position.id = tb_user.position
                    WHERE tb_user.uid = ?
                    LIMIT 1
                `
                let r = await mysqlUtil.execute(sql, [applyInfo.auditors[key].uid])
                if (r.length > 0) {
                    applyInfo.auditors[key].positionName = r[0].name
                }
            }
            callback({code: 0, message: '', data: applyInfo})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    // 加班申请
    jiabanApply: async (io, socket, params, callback) => {

        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc

        logger.debug('jiaban apply', params)

        let auditorsObj = {}
        for (let i = 0; i != params.auditors.length; i++) {
            auditorsObj['auditor' + i] = { uid: params.auditors[i].uid, stepName: params.auditors[i].stepName, sta: 0 }
        }

        try {
            let res = await mysqlUtil.execute('SELECT name FROM tb_user WHERE uid = ? LIMIT 1', [uid])
            if (res.length == 0) return callback({code: -1, message: '玩家不存在'})
            let myName = res[0].name

            let sql = 'INSERT INTO tb_jiaban_apply SET ?'
            let sqlParams = [{
                uid,
                sta: 0,
                auditSta: 1,
                department: params.department,
                fuzeren: params.fuzeren,
                startTime: moment(params.startTime).format('YYYY-MM-DD HH:mm:ss'),
                startWu: params.startWu || 0,
                endTime: moment(params.endTime).format('YYYY-MM-DD HH:mm:ss'),
                endWu: params.endWu || 0,
                shichang: params.shichang || '',
                reason: params.reason,
                attenders: JSON.stringify(params.attenders),
                auditors: JSON.stringify(auditorsObj),
                createTime: Date.now()
            }]
            res = await mysqlUtil.execute(sql, sqlParams)

            let applyId = res.insertId

            // 为审核人员添加任务
            await mysqlUtil.execute('INSERT INTO tb_user_task SET ?', [{
                uid: params.auditors[0].uid,
                typ: 2,
                relevenceId: applyId,
                pos: 0,
                title: '加班审批',
                content: myName + '的加班申请需要您审批',
                sta: 0,
                createTime: Date.now()
            }])
            io.in(params.auditors[0].uid + '-app').emit('TASK-CHANGE')
            io.in(params.auditors[0].uid + '-app').emit('TASK-NEW')
            io.in(params.auditors[0].uid + '-web').emit('TASK-CHANGE')
            io.in(params.auditors[0].uid + '-web').emit('TASK-NEW')
            util.sendReminderSmsToUser(params.auditors[0].uid, '加班申请', '待审批')

            callback({code: 0, message: 'success'})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    // 撤销请假申请
    revertJiabanApply: async (io, socket, params, callback) => {
        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc
        if (!params.id) return callback({code: -1, message: '参数缺失'})

        try {
            let res = await mysqlUtil.execute('UPDATE tb_jiaban_apply SET chexiao = 1 WHERE id = ? LIMIT 1', [params.id])
            if (res.affectedRows == 0) return ({code: -1, message: '撤销失败'})
            await mysqlUtil.execute('DELETE FROM tb_user_jiaban WHERE relevenceId = ? LIMIT 1', [params.id])
            // 将审核人员的任务状态改下 并通知前端
            res = await mysqlUtil.execute('SELECT id, uid FROM tb_user_task WHERE typ = 2 AND relevenceId = ?', [params.id])
            for (let i = 0; i != res.length; i++) {
                await mysqlUtil.execute('UPDATE tb_user_task SET sta = 3 WHERE id = ? LIMIT 1', [res[i].id])
                // 通知前端
                io.in(res[i].uid + '-app').emit('TASK-REVERT', res[i].id)
                io.in(res[i].uid + '-web').emit('TASK-REVERT', res[i].id)
            }
            callback({code: 0, message: ''})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    // 加班申请记录
    jiabanApplyRecord: async(io, socket, params, callback) => {
        let uid = socket.uid
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        callback = callback || commonModule.emptyFunc

        // 小于的id
        let lessThan = params.lessThan || 0
        // 大于的id
        let greaterThan = params.greaterThan || 0
        // 每页的数量
        let pageCount = params.pageCount || 10

        try {
            let sql = ''
            let sqlParams = []
            if (lessThan) {
                sql = 'SELECT * FROM tb_jiaban_apply WHERE uid = ? AND id < ? ORDER BY id DESC LIMIT ?'
                sqlParams = [uid, lessThan, pageCount]
            } else if (greaterThan) {
                sql = 'SELECT * FROM tb_jiaban_apply WHERE uid = ? AND id > ? ORDER BY id DESC LIMIT ?'
                sqlParams = [uid, greaterThan, pageCount]
            } else {
                sql = 'SELECT * FROM tb_jiaban_apply WHERE uid = ? ORDER BY id DESC LIMIT ?'
                sqlParams = [uid, pageCount]
            }
            let res = await mysqlUtil.execute(sql, sqlParams)
            callback({code: 0, message: 'success', data: res})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    jiabanApplyRecordWeb: async(io, socket, params, callback) => {

        let uid = socket.uid
        callback = callback || commonModule.emptyFunc
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        let pageIndex = parseInt(params.pageIndex || 1);
        let pageCount = parseInt(params.pageCount || 10);

        try {
            let res = await mysqlUtil.execute('SELECT COUNT(1) cnt FROM tb_jiaban_apply WHERE uid = ?', [uid])
            let count = res[0].cnt
            let sql = `
                SELECT 
                tb_jiaban_apply.*,
                tb_user.name fuzerenname
                FROM tb_jiaban_apply 
                INNER JOIN tb_user ON tb_user.uid = tb_jiaban_apply.fuzeren
                WHERE tb_jiaban_apply.uid = ? ORDER BY tb_jiaban_apply.id DESC LIMIT ?, ?
            `
            let params = [uid, (pageIndex - 1) * pageCount, pageCount]
            res = await mysqlUtil.execute(sql, params)
            callback({code: 0, message: 'success', data: {list: res, count}})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    jiabanApplyDetail: async(io, socket, params, callback) => {

        let uid = socket.uid
        callback = callback || commonModule.emptyFunc
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        if (!params.id) return callback({code: -1, message: '参数错误'})

        try {
            let res = await mysqlUtil.execute('SELECT * FROM tb_jiaban_apply WHERE id = ? LIMIT 1', [params.id])
            if (res.length == 0) return callback({code: -1, message: '不存在'})
            let applyInfo = res[0]
            applyInfo.auditors = applyInfo.auditors || []
            for (let key in applyInfo.auditors) {
                let sql = `
                    SELECT
                        con_position.name
                    FROM
                        tb_user
                    INNER JOIN con_position ON con_position.id = tb_user.position
                    WHERE tb_user.uid = ?
                    LIMIT 1
                `
                let r = await mysqlUtil.execute(sql, [applyInfo.auditors[key].uid])
                if (r.length > 0) {
                    applyInfo.auditors[key].positionName = r[0].name
                }
            }
            callback({code: 0, message: '', data: applyInfo})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    getUserDakaSetting: async(io, socket, params, callback) => {

        let uid = socket.uid
        callback = callback || commonModule.emptyFunc
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        try {
            let res = await mysqlUtil.execute('SELECT * FROM tb_user_daka_setting WHERE uid = ? LIMIT 1', [uid])
            let dakaSetting = {
                uid,
                shangbanAuto: 0,
                shangbanStart: 0,
                shangbanEnd: 0,
                xiabanAuto: 0,
                xiabanStart: 0,
                xiabanEnd: 0
            }
            if (res.length == 0) {
                await mysqlUtil.execute('INSERT INTO tb_user_daka_setting SET ?', [dakaSetting])
            } else {
                dakaSetting = res[0]
            }

            callback({code: 0, message: '', data: dakaSetting})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    updateUserDakaSetting: async (io, socket, params, callback) => {
        let uid = socket.uid
        callback = callback || commonModule.emptyFunc
        if (!uid) return callback({code: 9999, message: '请重新登录'})

        try {
            let sqlParams = [params.shangbanAuto || 0, params.shangbanStart || 0, params.shangbanEnd || 0, params.xiabanAuto || 0, params.xiabanStart || 0, params.xiabanEnd || 0, uid]
            await mysqlUtil.execute('UPDATE tb_user_daka_setting SET shangbanAuto = ?, shangbanStart = ?, shangbanEnd = ?, xiabanAuto = ?, xiabanStart = ?, xiabanEnd = ? WHERE uid = ? LIMIT 1', sqlParams)
            callback({code: 0, message: ''})
        } catch (err) {
            logger.error(err)
            callback({code: -1, message: 'system error'})
        }
    },

    // 生成每个用户的打卡记录表 从现在到下个月末 并根据节假日的表 设置其上班状态
    shengchengShangbanSta: async () => {
        try {
            // 查询所有在职用户
            let res = await mysqlUtil.execute('SELECT * FROM tb_user WHERE sta != "离职"')
            let allUsers = res
            // 计算从现在到下个月末的天数
            let diffDays = moment().add(1, 'month').endOf('month').diff(moment(), 'day')
            // 查询从现在到下个月末的节假日情况
            let sqlParams = [moment().format('YYYY-MM-DD'), moment().add(diffDays, 'day').format('YYYY-MM-DD')]
            res = await mysqlUtil.execute('SELECT riqi, typ FROM con_jiejiari WHERE riqi >= ? AND riqi <= ?', sqlParams)
            let jiejiariInfo = {}
            for (let i = 0; i != res.length; i++) {
                res[i].riqi = moment(res[i].riqi).format('YYYY-MM-DD')
                if (res[i].typ == 0) { // 上班
                    jiejiariInfo[res[i].riqi] = {id: 1, desc: '上班'}
                } else if (res[i].typ == 1 || res[i].typ == 2) { // 休息
                    jiejiariInfo[res[i].riqi] = {id: 0, desc: '休息'}
                }
            }
            // 添加考勤记录
            for (let i = 0; i != allUsers.length; i++) {
                let userInfo = allUsers[i]
                for (let j = 0; j != diffDays; j++) {
                    let dayStr = moment().add(j, 'day').format('YYYY-MM-DD')
                    let dakaInfo = {
                        uid: userInfo.uid,
                        day: dayStr,
                        sta: JSON.stringify(jiejiariInfo[dayStr] || {})
                    }
                    await mysqlUtil.execute('INSERT INTO tb_user_daka SET ? ON DUPLICATE KEY UPDATE day = ?', [dakaInfo, dakaInfo.day])
                }
            }
            logger.info('生成上班状态完成')
            return {code: 0, message: ''}
        } catch (err) {
            logger.error(err)
            return {code: -1, message: 'system error'}
        }
    },

    // 生成值班记录
    // 查询前一个休息日值班人的信息 根据其id以及性质 确定下一个排班人 如果是企业人员 则下一个就是在编人员排班 查询比这个id大的下一个id 如果没有则从头开始查找
    // 如果前一个休息日没有排班人员 则先从企业人员开始排班 企业人员第一个
    gengxinZhibanSta: async () => {
        try {

            // 获取系统参数中上次值班人员的信息
            let res = await mysqlUtil.execute('SELECT * FROM con_system_param WHERE name = "ZHIBAN_INFO" LIMIT 1')
            let zhibanInfo = {
                name: 'ZHIBAN_INFO',
                val: {
                    lastRiqi: '2022-01-01', // 上次值班日期
                    qiyeId: 0, // 上次企业人员值班id
                    zaibianId: 0, // 上次在编人员值班id
                },
                description: '值班信息'
            }
            if (res.length == 0) {
                await mysqlUtil.execute('INSERT INTO con_system_param SET ?', [{
                    name: zhibanInfo.name,
                    val: JSON.stringify(zhibanInfo.val),
                    description: zhibanInfo.description
                }])
            } else {
                zhibanInfo = res[0]
                zhibanInfo.val = JSON.parse(zhibanInfo.val)
            }

            // 计算从现在到下个月末的天数
            let diffDays = moment().add(1, 'month').endOf('month').diff(moment(), 'day')
            // 查询从现在到下个月末的节假日情况
            res = await mysqlUtil.execute('SELECT riqi, typ FROM con_jiejiari WHERE riqi >= ? AND riqi <= ?', [moment().format('YYYY-MM-DD'), moment().add(diffDays, 'day').format('YYYY-MM-DD')])
            let jiejiariInfo = {}
            for (let i = 0; i != res.length; i++) {
                res[i].riqi = moment(res[i].riqi).format('YYYY-MM-DD')
                if (res[i].typ == 0) { // 上班
                    jiejiariInfo[res[i].riqi] = {id: 1, desc: '上班'}
                } else if (res[i].typ == 1 || res[i].typ == 2) { // 休息
                    jiejiariInfo[res[i].riqi] = {id: 0, desc: '休息'}
                }
            }

            for (let i = 1; i != diffDays; i++) {
                let thatDay = moment().add(i, 'day').startOf('day')
                let thatDayStr = thatDay.format('YYYY-MM-DD')
                if (jiejiariInfo[thatDayStr].id == 0 && thatDay.isAfter(moment(zhibanInfo.val.lastRiqi).startOf('day'))) { // 上次排班后的下一个休息日期
                    // 下次值班的人员id
                    let nextZhibanId = 0

                    // 最后排班的日期
                    zhibanInfo.val.lastRiqi = thatDayStr

                    logger.debug(thatDayStr)

                    let zhibanUserInfo1, zhibanUserInfo2

                    {
                        // 获取下一个在编人员的id
                        let r = await mysqlUtil.execute('SELECT * FROM tb_user WHERE sta != "离职" AND xingzhi = "在编人员" AND id > ? ORDER BY id ASC LIMIT 1', [zhibanInfo.val.zaibianId])
                        if (r.length == 0) { // 没有了 从头开始排班
                            logger.debug('在编 没有了')
                            r = await mysqlUtil.execute('SELECT * FROM tb_user WHERE sta != "离职" AND xingzhi = "在编人员" ORDER BY id ASC LIMIT 1')
                            if (r.length == 0) {
                                return logger.error('排班失败，没有在编人员')
                            }
                            nextZhibanId = r[0].uid
                            zhibanInfo.val.zaibianId = r[0].id
                        } else {
                            nextZhibanId = r[0].uid
                            zhibanInfo.val.zaibianId = r[0].id
                        }

                        // 更新用户的打卡信息
                        await mysqlUtil.execute('UPDATE tb_user_daka SET sta = ? WHERE uid = ? AND day = ? LIMIT 1', [JSON.stringify({id: 2, desc: '值班'}), nextZhibanId, thatDayStr])

                        // 查询值班人员信息 更新到节假日表格中
                        logger.debug('next zhiban id', nextZhibanId)
                        zhibanUserInfo1 = await mysqlUtil.execute('SELECT * FROM tb_user WHERE uid = ? LIMIT 1', [nextZhibanId])
                        zhibanUserInfo1 = zhibanUserInfo1[0]
                    }

                    {
                        // 获取下一个企业人员的id
                        let r = await mysqlUtil.execute('SELECT * FROM tb_user WHERE sta != "离职" AND xingzhi = "企业人员" AND id > ? ORDER BY id ASC LIMIT 1', [zhibanInfo.val.qiyeId])
                        if (r.length == 0) { // 没有了 从头开始排班
                            r = await mysqlUtil.execute('SELECT * FROM tb_user WHERE sta != "离职" AND xingzhi = "企业人员" ORDER BY id ASC LIMIT 1')
                            if (r.length == 0) {
                                return logger.error('排班失败，没有企业人员')
                            }
                            nextZhibanId = r[0].uid
                            zhibanInfo.val.qiyeId = r[0].id
                        } else {
                            nextZhibanId = r[0].uid
                            zhibanInfo.val.qiyeId = r[0].id
                        }

                        // 更新用户的打卡信息
                        await mysqlUtil.execute('UPDATE tb_user_daka SET sta = ? WHERE uid = ? AND day = ? LIMIT 1', [JSON.stringify({id: 2, desc: '值班'}), nextZhibanId, thatDayStr])

                        // 查询值班人员信息 更新到节假日表格中
                        logger.debug('next zhiban id', nextZhibanId)
                        zhibanUserInfo2 = await mysqlUtil.execute('SELECT * FROM tb_user WHERE uid = ? LIMIT 1', [nextZhibanId])
                        zhibanUserInfo2 = zhibanUserInfo2[0]
                    }

                    // // 更新用户的打卡信息
                    // await mysqlUtil.execute('UPDATE tb_user_daka SET sta = ? WHERE uid = ? AND day = ? LIMIT 1', [JSON.stringify({id: 2, desc: '值班'}), nextZhibanId, thatDayStr])

                    // // 查询值班人员信息 更新到节假日表格中
                    // logger.debug('next zhiban id', nextZhibanId)
                    // let zhibanUserInfo = await mysqlUtil.execute('SELECT * FROM tb_user WHERE uid = ? LIMIT 1', [nextZhibanId])
                    // zhibanUserInfo = zhibanUserInfo[0]

                    await mysqlUtil.execute('UPDATE con_jiejiari SET zhiban = ? WHERE riqi = ? LIMIT 1', [JSON.stringify(
                        [{uid: zhibanUserInfo1.uid, name: zhibanUserInfo1.name, avatar: zhibanUserInfo1.avatar},
                        {uid: zhibanUserInfo2.uid, name: zhibanUserInfo2.name, avatar: zhibanUserInfo2.avatar}]
                        ), thatDayStr])
                }
            }

            await mysqlUtil.execute('UPDATE con_system_param SET val = ? WHERE name = "ZHIBAN_INFO"', [JSON.stringify(zhibanInfo.val)])
            logger.info('排班完成')
            return {code: 0, message: ''}
        } catch (err) {
            logger.error(err)
            return {code: -1, message: 'system error'}
        }
    }
}
